import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { DataSource } from 'typeorm';
import * as fs from 'fs';
import * as path from 'path';
import * as archiver from 'archiver';
import { SystemUtil } from '../utils/system.util';

/**
 * 数据库备份服务
 * 基于 TypeORM 实现
 * 对应 Java: DatabaseBackup
 */
@Injectable()
export class DatabaseBackupService {
  constructor(
    private readonly configService: ConfigService,
    private readonly dataSource: DataSource,
    private readonly systemUtil: SystemUtil,
  ) {}

  /**
   * 备份数据库
   */
  async backupDatabase(options?: {
    tables?: string[];
    excludeTables?: string[];
    outputPath?: string;
    compress?: boolean;
    includeData?: boolean;
    includeSchema?: boolean;
  }): Promise<{
    success: boolean;
    filePath: string;
    size: number;
    tables: number;
    message: string;
  }> {
    const config = {
      tables: options?.tables || [],
      excludeTables: options?.excludeTables || [],
      outputPath: options?.outputPath || this.getDefaultBackupPath(),
      compress: options?.compress !== false,
      includeData: options?.includeData !== false,
      includeSchema: options?.includeSchema !== false,
    };

    try {
      // 确保输出目录存在
      await this.systemUtil.createDirectory(path.dirname(config.outputPath));

      // 获取所有表
      const allTables = await this.getAllTables();
      const tablesToBackup = this.filterTables(
        allTables,
        config.tables,
        config.excludeTables,
      );

      if (tablesToBackup.length === 0) {
        throw new Error('没有找到要备份的表');
      }

      // 生成备份文件名
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const backupFileName = `backup_${timestamp}.sql`;
      const backupFilePath = path.join(config.outputPath, backupFileName);

      // 执行备份
      const backupContent = await this.generateBackupContent(
        tablesToBackup,
        config,
      );
      await this.systemUtil.writeFile(backupFilePath, backupContent);

      // 压缩备份文件
      let finalFilePath = backupFilePath;
      if (config.compress) {
        finalFilePath = await this.compressBackupFile(backupFilePath);
        // 删除原始文件
        await this.systemUtil.removeFileOrDirectory(backupFilePath);
      }

      // 获取文件信息
      const fileInfo = await this.systemUtil.getFileInfo(finalFilePath);

      return {
        success: true,
        filePath: finalFilePath,
        size: fileInfo.size,
        tables: tablesToBackup.length,
        message: '数据库备份成功',
      };
    } catch (error) {
      return {
        success: false,
        filePath: '',
        size: 0,
        tables: 0,
        message: `数据库备份失败: ${error.message}`,
      };
    }
  }

  /**
   * 恢复数据库
   */
  async restoreDatabase(
    backupFilePath: string,
    options?: {
      tables?: string[];
      excludeTables?: string[];
      dropTables?: boolean;
    },
  ): Promise<{
    success: boolean;
    message: string;
  }> {
    try {
      // 检查备份文件是否存在
      if (!(await this.systemUtil.fileExists(backupFilePath))) {
        throw new Error('备份文件不存在');
      }

      // 解压备份文件（如果需要）
      let sqlFilePath = backupFilePath;
      if (backupFilePath.endsWith('.zip')) {
        sqlFilePath = await this.extractBackupFile(backupFilePath);
      }

      // 读取 SQL 内容
      const sqlContent = await this.systemUtil.readFile(sqlFilePath);

      // 解析 SQL 内容
      const sqlStatements = this.parseSqlContent(sqlContent);

      // 执行恢复
      await this.executeRestore(sqlStatements, options);

      // 清理临时文件
      if (sqlFilePath !== backupFilePath) {
        await this.systemUtil.removeFileOrDirectory(sqlFilePath);
      }

      return {
        success: true,
        message: '数据库恢复成功',
      };
    } catch (error) {
      return {
        success: false,
        message: `数据库恢复失败: ${error.message}`,
      };
    }
  }

  /**
   * 获取所有表
   */
  private async getAllTables(): Promise<string[]> {
    const queryRunner = this.dataSource.createQueryRunner();
    try {
      const tables = await queryRunner.query(`
        SELECT TABLE_NAME 
        FROM INFORMATION_SCHEMA.TABLES 
        WHERE TABLE_SCHEMA = DATABASE()
        ORDER BY TABLE_NAME
      `);
      return tables.map((table: any) => table.TABLE_NAME);
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * 过滤表
   */
  private filterTables(
    allTables: string[],
    includeTables: string[],
    excludeTables: string[],
  ): string[] {
    let filteredTables = allTables;

    // 包含指定表
    if (includeTables.length > 0) {
      filteredTables = filteredTables.filter((table) =>
        includeTables.includes(table),
      );
    }

    // 排除指定表
    if (excludeTables.length > 0) {
      filteredTables = filteredTables.filter(
        (table) => !excludeTables.includes(table),
      );
    }

    return filteredTables;
  }

  /**
   * 生成备份内容
   */
  private async generateBackupContent(
    tables: string[],
    config: any,
  ): Promise<string> {
    const queryRunner = this.dataSource.createQueryRunner();
    let backupContent = '';

    try {
      // 添加备份头部信息
      backupContent += this.generateBackupHeader();

      // 备份表结构
      if (config.includeSchema) {
        for (const table of tables) {
          backupContent += await this.generateTableSchema(queryRunner, table);
        }
      }

      // 备份表数据
      if (config.includeData) {
        for (const table of tables) {
          backupContent += await this.generateTableData(queryRunner, table);
        }
      }

      // 添加备份尾部信息
      backupContent += this.generateBackupFooter();

      return backupContent;
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * 生成备份头部
   */
  private generateBackupHeader(): string {
    const timestamp = new Date().toISOString();
    return `
-- WWJCloud Database Backup
-- Generated at: ${timestamp}
-- Database: ${this.dataSource.options.database}
-- Version: ${this.dataSource.options.type}

SET FOREIGN_KEY_CHECKS=0;
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT=0;
START TRANSACTION;
SET time_zone = "+00:00";

`;
  }

  /**
   * 生成备份尾部
   */
  private generateBackupFooter(): string {
    return `
COMMIT;
SET FOREIGN_KEY_CHECKS=1;
`;
  }

  /**
   * 生成表结构
   */
  private async generateTableSchema(
    queryRunner: any,
    tableName: string,
  ): Promise<string> {
    const createTableResult = await queryRunner.query(
      `SHOW CREATE TABLE \`${tableName}\``,
    );
    const createTableSql = createTableResult[0]['Create Table'];

    return `
-- Table structure for table \`${tableName}\`
DROP TABLE IF EXISTS \`${tableName}\`;
${createTableSql};

`;
  }

  /**
   * 生成表数据
   */
  private async generateTableData(
    queryRunner: any,
    tableName: string,
  ): Promise<string> {
    const rows = await queryRunner.query(`SELECT * FROM \`${tableName}\``);

    if (rows.length === 0) {
      return `-- Data for table \`${tableName}\` (empty)\n\n`;
    }

    // 获取列名
    const columns = Object.keys(rows[0]);
    const columnNames = columns.map((col) => `\`${col}\``).join(', ');

    // 生成 INSERT 语句
    let insertStatements = `-- Data for table \`${tableName}\`\n`;

    for (const row of rows) {
      const values = columns
        .map((col) => {
          const value = row[col];
          if (value === null) return 'NULL';
          if (typeof value === 'string')
            return `'${value.replace(/'/g, "''")}'`;
          if (value instanceof Date)
            return `'${value.toISOString().slice(0, 19).replace('T', ' ')}'`;
          return value;
        })
        .join(', ');

      insertStatements += `INSERT INTO \`${tableName}\` (${columnNames}) VALUES (${values});\n`;
    }

    return insertStatements + '\n';
  }

  /**
   * 压缩备份文件
   */
  private async compressBackupFile(filePath: string): Promise<string> {
    const zipPath = filePath.replace('.sql', '.zip');

    return new Promise((resolve, reject) => {
      const output = fs.createWriteStream(zipPath);
      const archive = archiver('zip', { zlib: { level: 9 } });

      output.on('close', () => {
        resolve(zipPath);
      });

      archive.on('error', (err) => {
        reject(err);
      });

      archive.pipe(output);
      archive.file(filePath, { name: path.basename(filePath) });
      archive.finalize();
    });
  }

  /**
   * 解压备份文件
   */
  private async extractBackupFile(zipPath: string): Promise<string> {
    // 这里需要实现解压逻辑
    // 由于 archiver 主要用于压缩，解压需要使用其他库如 yauzl
    throw new Error('解压功能需要额外实现');
  }

  /**
   * 解析 SQL 内容
   */
  private parseSqlContent(sqlContent: string): string[] {
    // 简单的 SQL 语句分割
    return sqlContent
      .split(';')
      .map((stmt) => stmt.trim())
      .filter((stmt) => stmt.length > 0 && !stmt.startsWith('--'));
  }

  /**
   * 执行恢复
   */
  private async executeRestore(
    sqlStatements: string[],
    options?: any,
  ): Promise<void> {
    const queryRunner = this.dataSource.createQueryRunner();

    try {
      await queryRunner.startTransaction();

      for (const statement of sqlStatements) {
        if (statement.trim()) {
          await queryRunner.query(statement);
        }
      }

      await queryRunner.commitTransaction();
    } catch (error) {
      await queryRunner.rollbackTransaction();
      throw error;
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * 获取默认备份路径
   */
  private getDefaultBackupPath(): string {
    const backupDir = this.configService.get('database.backupPath', 'backups');
    return path.join(this.systemUtil.getAppRootDirectory(), backupDir);
  }

  /**
   * 获取备份列表
   */
  async getBackupList(): Promise<{
    files: Array<{
      name: string;
      path: string;
      size: number;
      createdAt: Date;
      modifiedAt: Date;
    }>;
  }> {
    const backupDir = this.getDefaultBackupPath();

    if (!(await this.systemUtil.fileExists(backupDir))) {
      return { files: [] };
    }

    const files = await this.systemUtil.readDirectory(backupDir);
    const backupFiles = files.filter(
      (file) => file.endsWith('.sql') || file.endsWith('.zip'),
    );

    const fileInfos = await Promise.all(
      backupFiles.map(async (file) => {
        const filePath = path.join(backupDir, file);
        const fileInfo = await this.systemUtil.getFileInfo(filePath);

        return {
          name: file,
          path: filePath,
          size: fileInfo.size,
          createdAt: fileInfo.ctime,
          modifiedAt: fileInfo.mtime,
        };
      }),
    );

    return { files: fileInfos };
  }

  /**
   * 删除备份文件
   */
  async deleteBackupFile(filePath: string): Promise<{
    success: boolean;
    message: string;
  }> {
    try {
      await this.systemUtil.removeFileOrDirectory(filePath);
      return {
        success: true,
        message: '备份文件删除成功',
      };
    } catch (error) {
      return {
        success: false,
        message: `备份文件删除失败: ${error.message}`,
      };
    }
  }
}
